// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
// SPDX-License-Identifier: MIT

package logger

import (
        "os"
	    "fmt"
        "time"
	    "path/filepath"
	    "uos-dovecot-exporter/pkg/utils"
)

var (
        defaultMaxFiles = 5
)

type FileRotator struct {
        basePath  string
        maxSize   int64
        maxAge    time.Duration
        current   *os.File
        size      int64
        startTime time.Time
        keepFiles int
}


func NewFileRotator(basePath string, maxSize int64, maxAge time.Duration) *FileRotator {
        dir := filepath.Dir(basePath)
        _, err := os.Stat(dir)
        if os.IsNotExist(err) {
		        if err := os.MkdirAll(dir, 0700); err != nil {
                        fmt.Printf("Warning: Failed to create the log directory: %v\n", err)
		        }
        }
        return &FileRotator{
                basePath:  basePath,
                maxSize:   maxSize,
                maxAge:    maxAge,
                keepFiles: defaultMaxFiles,
        }
}

func (fr *FileRotator) Write(p []byte) (n int, err error) {
        err = fr.setupCurrent()
        if err != nil {
                return 0, err
        }
	if fr.shouldRotate() {
		err = fr.rotate()
		if err != nil {
			return 0, err
		}
	}
        n, err = fr.current.Write(p)
        if err != nil {
                return n, err
        }
        fr.size += int64(n)
        return n, nil
}


func (fr *FileRotator) setupCurrent() error {
        if fr.current != nil {
                return nil
        }

        fileinfo, err := os.Stat(fr.basePath)
        switch {
                case err == nil:
                        fr.current, err = os.OpenFile(fr.basePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
                        if err != nil {
                                return fmt.Errorf("open existing file: %w", err)
                        }
                        fr.size = fileinfo.Size()
                        fr.startTime = fileinfo.ModTime()

                case os.IsNotExist(err):
                        fr.current, err = os.Create(fr.basePath)
                        if err != nil {
                                return fmt.Errorf("create new file: %w", err)
                        }
                        fr.size = 0
                        fr.startTime = time.Now()

                default:
                        return fmt.Errorf("check file status: %w", err)
        }

        return nil
}

func (fr *FileRotator) shouldRotate() bool {
	if fr.size > fr.maxSize || time.Now().Sub(fr.startTime) > fr.maxAge {
		return true
	}
	return false
}

func (fr *FileRotator) rotate() error {
	if fr.current != nil {
		err := fr.current.Close()
		if err != nil {
			return err
		}
	}

	for i := fr.keepFiles - 1; i > 0; i-- {
		if !utils.FileExists(fr.getLogPath(i)) {
			continue
		}
		if i == fr.keepFiles-1 {
			err := os.Remove(fr.getLogPath(i))
			if err != nil {
				return err
			}
			continue
		}
		err := os.Rename(fr.getLogPath(i), fr.getLogPath(i+1))
		if err != nil {
			return err
		}
	}
	err := os.Rename(fr.basePath, fr.getLogPath(1))
	if err != nil {
		return err
	}
	fr.current, err = os.Create(fr.basePath)
	if err != nil {
		return err
	}
	fr.size = 0
	fr.startTime = time.Now()
	return nil
}

func (fr *FileRotator) getLogPath(number int) string {
	return fmt.Sprintf("%s.%d", fr.basePath, number)
}

func (fr *FileRotator) Close() error {
        if fr.current != nil {
                return fr.current.Close()
        }
        return nil
}

